﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OpenPose.MyInterface;
using System;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;

namespace Score
{
    /// <summary>
    /// 存储一个运行时的npc的各项信息 在NPC开始跳舞时创建
    /// </summary>
    [Serializable]
    public class DancingNPC
    {
        // 将秒转化为TimeSpan的基本单位(100ns)
        public static long S2NS = (long)Mathf.Pow(10, 7);

        // npc资源路径 每个npc放在一个单独的文件夹中
        //public string resource_root;
        
        // 帧率
        public int frame_rate = 60;

        // 保存本段舞蹈的所有(来自openpose的)骨骼数据序列
        public PoseTimeSeries poses;

        // n个动作对应n+1项 其中第i项表示第i+1个动作的开始帧在poses中的索引;第0项为第一个动作的开始帧的索引
        // 则第i个动作对应的帧即为 poses[npc_poses_index[i-1]:npc_poses_index[i]-1],其中n>=i>=1
        // 注意一个动作的结束帧与下一个动作的开始帧是不一致的
        public int[] npc_poses_index;

        // npc_poses_index对应的时间戳
        //public TimeSpan[] npc_pose_split;

        // 表示NPC开始运行时的时间戳 
       //public DateTime starttime;

        //表示npc的动作总数
        public int num_motion;

        // 表示npc是否已激活(激活后,其所有动作帧的coresp_time皆为系统时间)
        //public bool status = false;

        // 用于读json
        public class Model
        {
            public float version;
            public List<People> people;
        }

        [Serializable]
        public class People
        {
            public float[] pose_keypoints_2d;
        }

        /// <summary>
        /// 给定npc资源路径 加载npc 注意此时npc的动作帧是未定时的 需要令其开始"跳舞"
        /// 本方法耗时较长 请调用Get_NPC
        /// </summary>
        /// <param name="path"></param>
        /// <param name="frame_rate"></param>
        private DancingNPC(string path, int frame_rate)
        { 
            this.frame_rate = frame_rate;
            //this.starttime = starttime;

            float[] x = new float[25];
            float[] y = new float[25];
            float[] score = new float[25];
            // 暂存数据
            float[] xt = new float[25];
            float[] yt = new float[25];
            float[] scoret = new float[25];
            //DateTime dt;
            int frame_mun = 0;
            //TimeSpan ts;

            // 读取全部json文件
            var files = Directory.GetFiles(path+"/openpose_output", "*.json");
            int frame_length = files.Length;
            poses = new PoseTimeSeries(frame_length);

            // 逐个解析
            foreach (var file in files)
            {
                frame_mun++;
                //ts = new TimeSpan((S2NS * frame_mun / frame_rate));
                //dt = starttime + ts;

                string jsonTest = File.ReadAllText(file, Encoding.UTF8);
                Model obj = JsonUtility.FromJson<Model>(jsonTest);
                if (obj.people != null)
                {
                    for (int i = 0; i < 25; i++)
                    {
                        x[i] = obj.people[0].pose_keypoints_2d[3 * i];
                        y[i] = obj.people[0].pose_keypoints_2d[3 * i + 1];
                        score[i] = obj.people[0].pose_keypoints_2d[3 * i + 2];
                    }
                    poses.add_poseKeyPoints(new PoseKeyPoints(25, DateTime.MaxValue, x, y, score));
                    xt = x;
                    yt = y;
                    scoret = score;
                }
                else
                {
                    // 如果没有检测到人就复制上一帧内容
                    poses.add_poseKeyPoints(new PoseKeyPoints(25, DateTime.MaxValue, xt, yt, scoret));
                }
            }


            //TO-DO 暂时均匀分割 每4秒算一个小动作
            int time_interval = 4;
            int flame_interval = time_interval * frame_rate;
            num_motion = poses.poseKeyPoints.Count / (flame_interval);
            npc_poses_index = new int[num_motion+1];
            
            //npc_pose_split = new TimeSpan[num_motion+1];
            for (int i = 0; i <= num_motion-1; i++)
            {
                npc_poses_index[i] = flame_interval  * i;
                //npc_pose_split[i] = new TimeSpan(time_interval*i*S2NS);
            }
            npc_poses_index[num_motion] = poses.poseKeyPoints.Count;

            //npc_pose_split[num_motion] = new TimeSpan(poses.poseKeyPoints.Count / frame_rate * S2NS);
        }


        /// <summary>
        /// 获取某个动作对应的骨骼片段 
        /// </summary>
        /// <param name="order">大于等于1</param>
        /// <returns></returns>
        public List<PoseKeyPoints> getMotion(int order)
        {
            return poses.poseInSeg(npc_poses_index[order - 1], npc_poses_index[order]- npc_poses_index[order - 1]);
        }

        /// <summary>
        /// 获取一个NPC(尚未开始跳舞的) 
        /// 如果资源目录下已有serialized.dat,直接加载;否则,读取目录下资源并创建一个npc
        /// 获得后,需要调用start_dancing使其开始运作
        /// </summary>
        /// <param name="resource_root"></param>
        /// <param name="time"></param>
        /// <param name="flame_rate"></param>
        /// <returns></returns>
        public static DancingNPC Get_NPC(string resource_root, int flame_rate=60)
        {
            var dat_path = resource_root + "/serialized.dat";
            if (File.Exists(dat_path))
            {
                try
                {
                   return DeSerialize(dat_path);
                }
                catch (TypeLoadException)
                {
                    
                    Debug.LogWarning("删除旧文件");
                }
            }
            var npc = new DancingNPC(resource_root, flame_rate);
            npc.Serialize(resource_root);
            return npc;
        }

        /// <summary>
        /// 将本实例序列化
        /// </summary>
        private void Serialize(string resource_root)
        {
            var db_path = resource_root + "/serialized.dat";
            FileStream fs = new FileStream(db_path, FileMode.Create);
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(fs, this);
            fs.Close();
        }

        private static DancingNPC DeSerialize(string db_path)
        {
            FileStream fs = new FileStream(db_path, FileMode.Open);
            BinaryFormatter bf = new BinaryFormatter();
            var npc =  bf.Deserialize(fs) as DancingNPC;
            fs.Close();
            //for (int i = 0; i < npc.npc_pose_split.Length; i++)
            //{
            //    npc.npc_pose_split[i] = npc.poses.poseKeyPoints[npc.npc_poses_index[i]].coresp_time- npc.poses.getStartTime();
            //}

            return npc;
        }

        public void start_dancing(DateTime time)
        {
            for (int i = 0; i < poses.poseKeyPoints.Count; i++)
            {
                poses.poseKeyPoints[i].coresp_time = time + new TimeSpan((S2NS * i / frame_rate));
            }
        }

    }

}


